using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PACKETPARSERMAX20.CORE;

namespace PACKETPARSERMAX20.CORE.DEFINITIONS
{
    public class SMSG_MONSTER_MOVE_TRANSPORT_DEF : DefinitionBase
    {

        

        public override bool Process()
        {
            long current_position = base.Stream.Position;

            if (ClientBuild >= 9056)
            {
                ProcessInternal_9056(current_position);
            }
            else
            {
                ProcessInternal(current_position);
            }
            return Validate();
        }

        public override bool Process(uint pParentOpcode, uint opcode, long pPosition, long pSize)
        {
            base.SetCustomPositionAndSize(pPosition, pSize);

            if (ClientBuild >= 9056)
            {
                ProcessInternal_9056(pPosition);
            }
            else
            {
                ProcessInternal(pPosition);
            }

            return ValidateMovementOpcode(pParentOpcode.ToString(), pPosition, pSize);
        }

        private void ProcessInternal(long pCurrentPosition)
        {
            base.Stream.Position = pCurrentPosition;

            MonsterMoveTransport tMonsterMove = new MonsterMoveTransport();
            tMonsterMove.PositionList = new SortedList<int, Coord3>();

            tMonsterMove.guid = popGuid();
            tMonsterMove.transportGuid = popGuid();
            //tMonsterMove.seatPos = ReadByte();
            tMonsterMove.positionInfo = ReadCoord3();

            tMonsterMove.ServerTime = ReadUInt32();

            tMonsterMove.type = ReadByte();

            tMonsterMove.count = 0;
            tMonsterMove.flags = 0;

            switch (tMonsterMove.type)
            {
                case 1:
                    break;
                case 2:
                    tMonsterMove.transportpositionInfo = ReadCoord3();
                    break;
                case 3:
                    tMonsterMove.targetGuid = ReadUInt64();
                    break;
                case 4:
                    tMonsterMove.targetRot = ReadSingle();
                    break;
                default:
                    break;
            }

            if (tMonsterMove.type == 1)
            {
                // do something, no data read
            }
            else
            {
                tMonsterMove.flags = ReadUInt32();  // movement flags
                if ((tMonsterMove.flags & 0x400000) > 0)
                {
                    tMonsterMove.byte_1 = ReadByte();
                    tMonsterMove.someMSTime = ReadUInt32();
                }
                tMonsterMove.moveTime = ReadUInt32();
                if ((tMonsterMove.flags & 0x8) > 0)
                {
                    tMonsterMove.float_1 = ReadSingle();
                    tMonsterMove.UInt32_1 = ReadUInt32();
                }
                tMonsterMove.count = ReadUInt32();
            }

            if (tMonsterMove.type == 1)
            {
                // do something, no data read
            }
            else
            {
                if (!((tMonsterMove.flags & 0x80200) > 0))
                {


                    Coord3 basenode = ReadCoord3();
                    tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, basenode);

                    if (tMonsterMove.count > 1)
                    {
                        if (tMonsterMove.count != 1) // wtf? blizz paranoic?
                        {
                            for (int i = 0; i < tMonsterMove.count - 1; ++i)
                            {
                                tMonsterMove.packedFloat = ReadInt32();
                                // unpacking
                                float offsetX = (float)((tMonsterMove.packedFloat & 0x7FF) << 21 >> 21) * 0.25f;
                                float offsetY = (float)((((tMonsterMove.packedFloat >> 11) & 0x7FF) << 21) >> 21) * 0.25f;
                                float offsetZ = (float)((tMonsterMove.packedFloat >> 22 << 22) >> 22) * 0.25f;

                                Coord3 newnode = new Coord3(basenode.X + offsetX, basenode.Y + offsetY, basenode.Z + offsetZ);

                                tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, newnode);

                                // unpacked
                            }
                        }
                    }
                }
                else
                {
                    Coord3 basenode = ReadCoord3();
                    tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, basenode);

                    if (tMonsterMove.count > 1)
                    {
                        for (int i = 0; i < tMonsterMove.count - 1; ++i)
                        {
                            float finalX = ReadSingle();
                            float finalY = ReadSingle();
                            float finalZ = ReadSingle();
                            tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, ReadCoord3());

                        }
                    }
                }
            }

            tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, tMonsterMove.positionInfo);

            foreach (Coord3 tNode in tMonsterMove.PositionList.Values)
            {
                UpdateObjectPosition(tMonsterMove.guid, tNode, tMonsterMove.ServerTime);
                break;
            }

        }

        private void ProcessInternal_9056(long pCurrentPosition)
        {
            base.Stream.Position = pCurrentPosition;

            MonsterMoveTransport tMonsterMove = new MonsterMoveTransport();
            tMonsterMove.PositionList = new SortedList<int,Coord3>();

            tMonsterMove.guid = popGuid();
   
            tMonsterMove.transportGuid = popGuid();
            if (ClientBuild > 9767)
            {
                byte unkb1 = ReadByte();                                       // new in 3.1
            }
            tMonsterMove.seatPos = ReadByte();

            tMonsterMove.positionInfo = ReadCoord3();

            tMonsterMove.ServerTime = ReadUInt32(); // tickCount
            tMonsterMove.type = ReadByte();
            tMonsterMove.count = 0;
            tMonsterMove.flags = 0;

            switch (tMonsterMove.type)
            {
                case 1:
                    break;
                case 2:
                    tMonsterMove.transportpositionInfo = ReadCoord3();
                    break;
                case 3:
                    tMonsterMove.targetGuid = ReadUInt64();
                    break;
                case 4:
                    tMonsterMove.targetRot = ReadSingle();
                    break;
                default:
                    break;
            }

            if (tMonsterMove.type == 1)
            {
                // do something, no data read
            }
            else
            {
                tMonsterMove.flags = ReadUInt32();  // movement flags
                if ((tMonsterMove.flags & 0x400000) > 0)
                {
                    tMonsterMove.byte_1 = ReadByte();
                    tMonsterMove.someMSTime = ReadUInt32();
                }
                tMonsterMove.moveTime = ReadUInt32();
                if ((tMonsterMove.flags & 0x8) > 0)
                {
                    tMonsterMove.float_1 = ReadSingle();
                    tMonsterMove.UInt32_1 = ReadUInt32();
                }
                tMonsterMove.count = ReadUInt32();
            }

            if (tMonsterMove.type == 1)
            {
                // do something, no data read
            }
            else
            {
                if (!((tMonsterMove.flags & 0x80200) > 0))
                {


                    Coord3 basenode = ReadCoord3();
                    tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, basenode);

                    if (tMonsterMove.count > 1)
                    {
                        if (tMonsterMove.count != 1) // wtf? blizz paranoic?
                        {
                            for (int i = 0; i < tMonsterMove.count - 1; ++i)
                            {
                                tMonsterMove.packedFloat = ReadInt32();
                                // unpacking
                                float offsetX = (float)((tMonsterMove.packedFloat & 0x7FF) << 21 >> 21) * 0.25f;
                                float offsetY = (float)((((tMonsterMove.packedFloat >> 11) & 0x7FF) << 21) >> 21) * 0.25f;
                                float offsetZ = (float)((tMonsterMove.packedFloat >> 22 << 22) >> 22) * 0.25f;

                                Coord3 newnode = new Coord3(basenode.X + offsetX, basenode.Y + offsetY, basenode.Z + offsetZ);

                                tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, newnode);

                                // unpacked
                            }
                        }
                    }
                }
                else
                {
                    Coord3 basenode = ReadCoord3();
                    tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, basenode);

                    if (tMonsterMove.count > 1)
                    {
                        for (int i = 0; i < tMonsterMove.count - 1; ++i)
                        {
                            float finalX = ReadSingle();
                            float finalY = ReadSingle();
                            float finalZ = ReadSingle();
                            tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, ReadCoord3());

                        }
                    }
                }
            }

            tMonsterMove.PositionList.Add(tMonsterMove.PositionList.Count, tMonsterMove.positionInfo);

            foreach (Coord3 tNode in tMonsterMove.PositionList.Values)
            {
                UpdateObjectPosition(tMonsterMove.guid, tNode, tMonsterMove.ServerTime);
                break;
            }


        }
    }
}

